#pragma once
#include "vmp.h"

namespace yzrilyzr_vmp{
	EBCLASS(BufferReader){
	private:
	const uint8_t * buf;
	const u_index length;
	u_index pointer;
	void checkIndex(u_index len){
		if(pointer + len > length)throw std::out_of_range("End of buffer");
	}

	public:
	BufferReader(uint8_t * buf, u_index length) :buf(buf), length(length){
		if(buf == nullptr) throw std::invalid_argument("Buffer cannot be null");
		pointer=0;
	}
	void seek(u_index pos){
		if(pos < 0 || pos >= length)throw std::out_of_range("Invalid pos");
		pointer=pos;
	}
	bool has(){
		return pointer < length;
	}
	uint32_t readUInt(){
		checkIndex(4);
		uint32_t v=0;
		v=buf[pointer++] & 0xff;
		v=(v << 8) | (buf[pointer++] & 0xff);
		v=(v << 8) | (buf[pointer++] & 0xff);
		v=(v << 8) | (buf[pointer++] & 0xff);
		return v;
	}
	int32_t readInt(){
		return static_cast<int32_t>(readUInt());
	}
	uint64_t readULong(){
		checkIndex(8);
		uint64_t v=0;
		v=buf[pointer++] & 0xff;
		v=(v << 8) | (buf[pointer++] & 0xff);
		v=(v << 8) | (buf[pointer++] & 0xff);
		v=(v << 8) | (buf[pointer++] & 0xff);
		v=(v << 8) | (buf[pointer++] & 0xff);
		v=(v << 8) | (buf[pointer++] & 0xff);
		v=(v << 8) | (buf[pointer++] & 0xff);
		v=(v << 8) | (buf[pointer++] & 0xff);
		return v;
	}
	int64_t readLong(){
		return static_cast<int64_t>(readULong());
	}
	uint8_t readUByte(){
		checkIndex(1);
		uint8_t v=buf[pointer++] & 0xff;
		return v;
	}
	int8_t readByte(){
		return static_cast<int8_t>(readUByte());
	}
	u_index readFully(uint8_t * arr, u_index len){
		return readFully(arr, 0, len);
	}
	u_index readFully(uint8_t * arr, u_index off, u_index len){
		checkIndex(len);
		memcpy(arr + off, buf + pointer, sizeof(uint8_t) * len);
		pointer+=len;
		return len;
	}
	std::string readString(){
		uint32_t length=readUInt();
		uint8_t * arr=new uint8_t[length+1];
		readFully(arr, length);
		arr[length]='\0';
		std::string string1((char*)arr);
		delete arr;
		return string1;
	}
	u_index getPointer(){
		return pointer;
	}
	};
}